<template>
  <div class="user_container">
    <!-- 面包屑导航 -->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>用户管理</el-breadcrumb-item>
      <el-breadcrumb-item>用户列表</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 卡片视图区域 -->
    <el-card>
      <!-- 使用layout栅格系统,固定宽度布局 总共24列（bootstrap 12列），:span指定跨多少列，:gutter设置相邻两列间距,默认为0--->
      <!-- 用户搜索和添加数据 -->
      <el-row :gutter="20">
        <el-col :span="8">
          <!--搜索功能：1.在input框中将查询字符串query变量，做双向绑定，以使得输入关键字后，query值立即同步到page对象中
          2.为el-button绑定click事件，事件处理函数仍是getUserList
          3.优化搜索功能，点击查询后，文本框尾部加一个清空按钮，点击清空文本框内容，重新显示所有数据
          添加一个clearable属性，就为文本框增加了尾部清空按钮，再为文本框绑定个clear事件即可
          -->
          <el-input placeholder="请输入内容" v-model="page.query" clearable @clear="getUserList">
            <el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button>
          </el-input>
        </el-col>
        <el-col :span="4">
          <!---添加用户按钮，绑定click事件，使得 dialogVisible: true-->
          <el-button type="primary" @click="showDialog">添加用户</el-button>
        </el-col>
      </el-row>
      <!-- 用户列表区 -->
      <el-table :data="usersList" style="width: 100%" class="users_table" border stripe>
        <!--type="index"就为表格加了索引列，默认从1开始，就是给表格每个tr行在第一个td位置加上连续序号。类比：type="expand"扩展列-->
        <el-table-column type="index" label="#"></el-table-column>
        <el-table-column prop="username" label="姓名"> </el-table-column>
        <el-table-column prop="email" label="邮箱"> </el-table-column>
        <el-table-column prop="mobile" label="电话"> </el-table-column>
        <el-table-column prop="role_name" label="角色"> </el-table-column>
        <el-table-column label="状态">
          <!--注意：对于el-table组件的使用，其中prop就是td格子中要渲染的内容。但是，当mg_state为布尔值，是无法直接在页面上渲染的，
          这里介绍vue的作用域插槽，在当前位置添加一个模板，等于直接放在了子组件el-table原本一对slot标签所在位置，
          模板可以理解成父组件渲染在子组件el-table中内容，则模板中可以拿到子组件中绑定的数据-->
          <template slot-scope="_user">
            <!--_user.row rou关键字可以拿到当前行的绑定的data中的数据-->
            <!-- {{ _user.row }}-->
            <!--把开关组件 放在模板中，可以拿到_user.row中的值。开关的作用：打开，该账号登录时，才能成功。
            否则，开关关闭，该行所在账号被锁定，无法登录-->
            <el-switch v-model="_user.row.mg_state" active-color="#409EFF" inactive-color="#DCDFE6" @change="userStateChanged(_user.row)"> </el-switch>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="180px">
          <!--自定义插槽-->
          <template slot-scope="_item">
            <!--修改按钮-点击该按钮，弹出修改对话框。对话框中渲染出用户名、邮箱、手机号信息，但是用户名是不能更改的-->
            <el-button type="primary" icon="el-icon-edit" size="mini" @click="shiftUserDialog(_item.row.id)"></el-button>
            <!--删除按钮-->
            <el-button type="danger" icon="el-icon-delete" size="mini" @click="deleteUserItem(_item.row.id)"></el-button>
            <!--分配角色 需求：分配权限图标不易作出识别，可以给其加一个提示，Tooltip文字组件。功能：设置鼠标不可进入提示文本区域，
            重置enterable属性为false(默认为true)，这样鼠标一旦进入，“提示文字将隐藏”-->
            <el-tooltip class="item" effect="dark" content="分配角色" placement="top" :enterable="false">
              <el-button type="warning" icon="el-icon-setting" size="mini" @click="showRoleDialog(_item.row)"></el-button>
            </el-tooltip>
          </template>
        </el-table-column>
      </el-table>
      <!--分页 page-sizes数组可以指定每页的容量，可自己定义，设置成数组[1,2,5],表示pagesize可以取1,2,5三个值-->
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="page.pagenum"
        :page-sizes="[1, 2, 5]"
        :page-size="page.pagesize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
      >
      </el-pagination>
    </el-card>
    <!--点击【添加用户】按钮弹出的对话框-->
    <el-dialog title="添加用户" :visible.sync="dialogVisible" width="50%" @close="addDialogClose">
      <span>
        <!--填写表单时，如果未完成或者合法完成表单内容填写，就关闭了对话框。对话框
      下次开启时，若希望得到一个全新的表单（清除上次残存的表单内容和填写验证状态提示），
      就使用form表单原生对象的 resetFields()方法，重置表单域-->
        <el-form :model="userForm" :rules="rules" ref="ruleForm" label-width="100px">
          <el-form-item label="用户名" prop="username">
            <el-input v-model="userForm.username"></el-input>
          </el-form-item>
          <el-form-item label="密码" prop="password">
            <el-input v-model="userForm.password" type="password"></el-input>
          </el-form-item>
          <el-form-item label="邮箱" prop="email" type="email">
            <el-input v-model="userForm.email"></el-input>
          </el-form-item>
          <el-form-item label="手机" prop="mobile">
            <el-input v-model="userForm.mobile"></el-input>
          </el-form-item>
        </el-form>
      </span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <!--点击【确定】按钮，不是直接dialogVisible = false，关闭对话框，
        而是要使用原生的validate()方法进行预校验-->
        <el-button type="primary" @click="addUserPre">确 定</el-button>
      </span>
    </el-dialog>
    <!--修改用户信息对话框,close事件重置el-form表单-->
    <el-dialog title="修改用户" :visible.sync="userDialogVisible" width="50%" @close="editFormReset">
      <span>
        <!--填写表单时，如果未完成或者合法完成表单内容填写，就关闭了对话框。对话框
      下次开启时，若希望得到一个全新的表单（清除上次残存的表单内容和填写验证状态提示），
      就使用form表单原生对象的 resetFields()方法，重置表单域-->
        <el-form :model="editForm" :rules="rules" ref="editFormRef" label-width="100px">
          <!--用户名信息已经禁用，不用更改，也就不需要验证，其prop属性可删除-->
          <el-form-item label="用户名">
            <el-input v-model="editForm.username" disabled></el-input>
          </el-form-item>
          <el-form-item label="邮箱" prop="email" type="email">
            <el-input v-model="editForm.email"></el-input>
          </el-form-item>
          <el-form-item label="手机" prop="mobile">
            <el-input v-model="editForm.mobile"></el-input>
          </el-form-item>
        </el-form>
      </span>
      <span slot="footer" class="dialog-footer">
        <!--【取消】按钮被点击后，原来未提交的修改信息，会保存在文本框内。下次再
      修改时，还需要手动删除文本域中的内容。使用对话框el-dialog的close事件，重置表单-->
        <el-button @click="userDialogVisible = false">取 消</el-button>
        <!--点击【确定】按钮，不是直接dialogVisible = false，关闭对话框，
        而是要使用原生的validate()方法进行预校验-->
        <el-button type="primary" @click="editUserPre">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 点击【分配角色】按钮，弹出的对话框 -->
    <el-dialog title="分配角色" :visible.sync="setRoleDialogVisible" width="50%" @close="resetSelectedAndInfo">
      <p>当前的用户：{{ userInfo.username }}</p>
      <p>当前的角色：{{ userInfo.role_name }}</p>
      <p>
        分配新角色：
        <!-- select标签中v-model的值是options的item.id值 -->
        <el-select v-model="rolesId" placeholder="请选择">
          <el-option v-for="item in rolesList" :key="item.id" :label="item.roleName" :value="item.id"> </el-option>
        </el-select>
      </p>
      <span slot="footer" class="dialog-footer">
        <el-button @click="setRoleDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="saveRoleInfo">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  data() {
    // 自定义邮箱的校验规则
    var emailCheck = (rule, value, cb) => {
      // 邮箱正则表达式
      const regEmail = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
      if (!regEmail.test(value)) {
        cb(new Error('请输入合法的邮箱地址'));
      }
      return cb();
    };
    // 自定义手机号的校验规则
    var mobileCheck = (rule, value, cb) => {
      // 手机号正则表达式
      const regTel = /^1([38][0-9]|4[5-9]|5[0-3,5-9]|66|7[0-8]|9[89])[0-9]{8}$/;
      if (!regTel.test(value)) {
        // 传入一个错误提示对象new Error()
        cb(new Error('请输入合法的手机号'));
      }
      return cb();
    };
    return {
      /*
      路径：users
      参数1：query   查询参数，可以为空,在上方文本输入框输入的查询字符串
      参数2：pagenum 当前页码，不能为空
      参数3：pagesize 每页显示条数，不能为空，可以取大于等于1的任何整数值，后台数据接口接收到请求参数pagesize后，会用total和pagesize进行计算
      */
      page: {
        query: '',
        pagenum: 1,
        pagesize: 2
      },
      // 获取
      usersList: [],
      // 获取总数据条数
      total: 0,
      // [添加用户]弹出的对话框默认隐藏
      dialogVisible: false,
      // 修改用户信息-弹出的对话框默认隐藏
      userDialogVisible: false,
      // 点击【分配角色】按钮，弹出的对话框的状态码
      setRoleDialogVisible: false,
      // 定义一个对象，存储当前用户信息
      userInfo: {},
      // 添加用户-对话框表单数据
      userForm: {
        username: '',
        password: '',
        email: '',
        mobile: ''
      },
      // 修改用户信息-弹出对话框数据
      editForm: {},
      rules: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { min: 3, max: 16, message: '长度在 3 到 16 个字符', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 3, max: 16, message: '长度在 3 到 16 个字符', trigger: 'blur' }
        ],
        email: [
          { required: true, message: '请输入邮箱地址', trigger: 'blur' },
          {
            validator: emailCheck,
            // message: '长度在 5 到 20 个字符',
            trigger: 'blur'
          }
        ],
        mobile: [
          { required: true, message: '请输入手机号', trigger: 'blur' },
          { validator: mobileCheck, trigger: 'blur' }
        ]
      },
      // 存储角色信息列表，要渲染到【分配角色】对话框下拉框中
      rolesList: [],
      // select选择器v-model属性值，可以自定义名称
      rolesId: ''
    };
  },
  created() {
    this.getUserList();
  },
  methods: {
    // 获取用户信息列表
    async getUserList() {
      const { data: res } = await this.$http('users', {
        params: this.page
      });
      if (res.meta.status !== 200) return this.$message.error(res.meta.msg);
      console.log(res);
      this.usersList = res.data.users;
      this.total = res.data.total;
      // 每页数据容量以及总数据条目，标签属性中，绑定了总数据条数total以及每页的容量pagesize,需要分多少页标签里已经做了封装
      console.log(this.usersList);
    },
    // 监听pagesize改变的事件，可以拿到页面容量
    handleSizeChange(val) {
      // val是pagesize值
      this.page.pagesize = val;
      this.getUserList();
      // console.log(`每页 ${val} 条`);
    },
    // 当前所在页码值，是pagenum
    handleCurrentChange(val) {
      // val当前页面值
      // console.log(`当前页: ${val}`);
      this.page.pagenum = val;
      this.getUserList();
    },
    // 修改用户状态信息
    async userStateChanged(data) {
      /* 开关状态改变事件change-事件处理函数 */
      // 尝试切换开关，vue作用域插槽中的数据对象mg_state的值随之改变
      console.log(data.mg_state);
      const { data: res } = await this.$http.put(`users/${data.id}/state/${data.mg_state}`);
      if (res.meta.status !== 200) {
        // 操作状态失败，将data.mg_state取反，设定成原来值即可
        data.mg_state = !data.mg_state;
      }
      // console.log(res.data.mg_state); //0或1是返回值，0表示false,1表示true
      // 提交数据成功，给一个提示
      this.$message.success(res.meta.msg);
    },
    // 【添加用户】按钮点击，弹出对话框
    showDialog() {
      this.dialogVisible = true;
    },
    // 关闭【添加用户】对话框时的回调
    addDialogClose() {
      // 重置表单域原生方法 resetFields()
      this.$refs.ruleForm.resetFields();
    },
    // 给【确定】按钮，添加原生预校验方法validate(),校验通过后就发起数据请求
    addUserPre() {
      this.$refs.ruleForm.validate(async valid => {
        // console.log(valid);//valid是个布尔值，预校验通过为true,反之为false
        if (!valid) return;
        const { data: res } = await this.$http.post('users', this.userForm);
        console.log(res);
        if (res.meta.status !== 201) this.$message.error(res.meta.msg);
        this.$message.success(res.meta.msg);
        // 添加成功后，关闭对话框
        this.dialogVisible = false;
        // 添加用户到后台号，让浏览器重新刷新页面，把新用户信息渲染在页面上
        this.getUserList();
      });
    },
    // 功能：点击【修改】按钮，弹出修改用户对话框，同时把数据渲染到对话框中
    /* 方式1：参数传入_item.row对象，直接从作用域插槽拿数据 */
    // shiftUserDialog(data) {
    //   //data是vue作用域插槽父组件中定义的变量
    //   this.userDialogVisible = true;
    //   // 把数据渲染到对话框中
    //   console.log(data);
    //   this.userForm.username = data.username;
    //   this.userForm.email = data.email;
    //   this.userForm.mobile = data.mobile;
    // }
    /* 方式2：参数传入_item.row.id，从数据接口请求数据 */
    async shiftUserDialog(id) {
      console.log(id);

      // 根据id请求数据
      const { data: res } = await this.$http.get('users/' + id);
      console.log(res);
      if (res.meta.status !== 200) this.$message.error(res.meta.msg);
      // 对话框弹出前，请求当前修改所在行的数据
      this.editForm = res.data;
      // 弹出对话框
      this.userDialogVisible = true;
    },
    /*
    修改用户信息对话框关闭事件，将修改后的规则验证提示去掉。和【添加用户】操作对话框不同，由于修改用户弹出对话框时，会请求数据接口，将数据渲染
    到表单中。因此即使重置，只是重置了红色验证规则提示文本。修改未提交的新内容，已经在对话框弹出时，通过请求数据接口实现了重置
    */
    editFormReset() {
      this.$refs.editFormRef.resetFields();
    },
    // 修改用户信息-表单预校验,验证成功之后，才提交请求
    editUserPre() {
      this.$refs.editFormRef.validate(async valid => {
        // console.log(valid);
        // 验证未通过
        if (!valid) return;
        // 验证通过，发起提交请求
        // 第352行弹出对话框请求数据，得到的editForm对象，包含该条数据的id值
        const { data: res } = await this.$http.put(`users/${this.editForm.id}`, {
          email: this.editForm.email,
          mobile: this.editForm.mobile
        });
        if (res.meta.status !== 200) return this.$message.error(res.meta.msg);
        // 调用数据请求getUserList刷新页面
        // 关闭对话框
        this.userDialogVisible = false;
        // 刷新数据列表
        this.getUserList();
        // 提示修改成功
        this.$message.success(res.meta.msg);
      });
    },
    // 删除当前所在行数据
    async deleteUserItem(id) {
      // 先弹框询问用户是否删除？
      const resMsg = await this.$messageBox
        .confirm('此操作将永久删除该文件, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        })
        .catch(error => error);
      // 1.如果用户点击【确认】，则 res返回值为confirm
      // 2.如果用户点击【取消】，则res返回值为cancel
      // console.log(resMsg); //接收这个函数的返回值
      if (resMsg !== 'confirm') return this.$message.error('删除操作已取消!');
      // 此处开始，是点击【确认】按钮的情况
      const { data: res } = await this.$http.delete(`users/${id}`);
      if (res.meta.status !== 200) return this.$message.error(res.meta.msg);
      // console.log(res);
      // 刷新页面，重新渲染数据
      this.getUserList();
      this.$message.success('已成功删除该条数据!');
    },
    // 点击【分配角色】按钮，弹出对话框
    // data为当前操作行的数据
    async showRoleDialog(data) {
      console.log(data);
      this.userInfo = data;
      // 在展示对话框前获取所有角色列表
      const { data: res } = await this.$http.get('roles');
      // console.log(res);
      if (res.meta.status !== 200) {
        return this.$message.error('获取角色列表失败！');
      }
      // 2.成功获取角色列表
      // 2.1 将角色列表渲染到对话框中
      this.rolesList = res.data;
      console.log(this.rolesList);
      // 弹出对话框
      this.setRoleDialogVisible = true;
    },
    // 点击【分配角色】对话框，select选择器选择一个角色后提交
    async saveRoleInfo() {
      // 选择器v-model捆绑值，其含义是当前选择的“角色名称”
      console.log(this.rolesId);
      if (!this.rolesId) return this.$message.error('请选择要分配的角色！');
      // 已经选择了一个角色选项
      // 注意：Select选选择器中，当选择了某个子项后，v-model中的roles值等于el-option选项中绑定的value属性值
      const { data: res } = await this.$http.put(`users/${this.userInfo.id}/role`, {
        rid: this.rolesId
      });
      console.log(res);
      // 1.更新用户角色失败
      if (res.meta.status !== 200) return this.$message.error('更新用户角色失败!');
      // 2.更新用户角色成功
      // 2.1 关闭对话框
      this.setRoleDialogVisible = false;
      // 2.2 重新调用用户列表
      this.getUserList();
      // 2.3 弹出更新角色成功提示
      this.$message.success('更新用户角色成功!');
    },
    // 上面一个方法，成功将修改的用户角色数据提交到后台。但是select选择器中选项在对话框关闭后，没有重置。
    // 需要设置对话框的关闭事件-close事件
    resetSelectedAndInfo() {
      // 重置el-select的v-model属性值为空，下拉列表回到默认的“请选择”子项
      this.rolesId = '';
      this.userInfo = {};
    }
  }
};
</script>

<style lang="less" scoped>
/* 面包屑导航样式，多处用到，将其置于公共样式global.less中 */
/* 弹出对话框样式，放在global.less中复用 */
</style>
